package com.hq.ims.data.interceptor.sql;


import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.hq.ims.data.entityenum.EnumCompanyLevel;
import com.hq.ims.data.interiorenum.EnumInsideFindDataScore;
import com.hq.ims.data.service.BaseCompanyService;
import com.hq.ims.data.utils.CurThreadLocalUtil;
import com.hq.mybatis.base.interceptor.SqlAnalysis;
import com.hq.mybatis.utils.sql.ExecutorPluginUtils;
import com.hq.utils.util.ListUtils;
import com.hq.utils.util.StringUtils;
import com.hq.web.utils.ApplicationContextHelper;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Slf4j
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
public class DataRangeInterceptor implements Interceptor {

    private final String CREATE_USER_ID_COL_NAME = "create_user_id";
    private final String OWN_ORG_ID = "org_id";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object parameterObj = invocation.getArgs()[1];
        BoundSql boundSql = (BoundSql) invocation.getArgs()[5];
        // 获取原始sql
        String oriSql = boundSql.getSql();
        //忽略sql中包含on conflict的情况

        if (!this.isNeedFilterData()) {
            return invocation.proceed();
        }
        try {
            CurThreadLocalUtil.ThreadStore threadStore = CurThreadLocalUtil.getInstance().get();
            String curCompanyId = threadStore.getOrgId();
            if (StringUtils.isEmpty(curCompanyId)) {
                return invocation.proceed();
            }

            //获取当前公司
            Statement statement = CCJSqlParserUtil.parse(oriSql);
            Select selectStatement = (Select) statement;
            PlainSelect plain = (PlainSelect) selectStatement.getSelectBody();
            FromItem fromItem = plain.getFromItem();
            String tableName = fromItem.toString().trim();
            String tableAlias = fromItem.getAlias() == null ? "" : fromItem.getAlias().getName();

            //是否自定义sqlPage
            boolean isCustomizeSqlPage = tableName.startsWith("(SELECT");
            SqlAnalysis sqlAnalysis = null;
            String outerLayerSql = oriSql;
            if (isCustomizeSqlPage) {
                String sql = statement.toString();
                int startIndex = sql.indexOf("(SELECT") + 1;
                int endIndex = sql.lastIndexOf(")");
                //截取最外层sql
                outerLayerSql = sql.substring(0, startIndex) + " %s " + sql.substring(endIndex);
                //分析内层sql
                String childSql = sql.substring(startIndex, endIndex);
                sqlAnalysis = new SqlAnalysis(childSql);
                tableName = sqlAnalysis.getTableName();
                tableAlias = sqlAnalysis.getTableAlias();
            }

            //是否存在表信息
            TableInfo cacheTable = this.getTableInfo(tableName);
            if (cacheTable == null) {
                return invocation.proceed();
            }

            String inSqlFieldName = this.getValidDataLibField(threadStore);
            //如果没有相关字段就跳过拦截,如果有就设置条件
            if (!this.isExistsAssignColumn(cacheTable, Arrays.asList(inSqlFieldName))) {
                return invocation.proceed();
            }

            //拼接where
            //获取in的范围值
            String inSqlValue = this.inSqlValue(curCompanyId, threadStore.getOrgLv(), threadStore.getFindDataScore());
            StringBuilder whereSql = new StringBuilder();
            whereSql.append(String.format("%s in (%s)", StringUtils.isEmpty(tableAlias) ? inSqlFieldName : tableAlias + "." + inSqlFieldName, inSqlValue));

            //接接新的sql
            String newSql = oriSql;
            if (isCustomizeSqlPage) {
                sqlAnalysis.getSqlWhereBefore();
                sqlAnalysis.getSqlWhereAfter();
                newSql = String.format(outerLayerSql, String.format("%s where %s %s", sqlAnalysis.getSqlWhereBefore(), whereSql.toString(), sqlAnalysis.getSqlWhereAfter().trim().length() > 0 ? " and " + sqlAnalysis.getSqlWhereAfter() : ""));
            } else {
                //拼接where
                Expression where = plain.getWhere();
                //是否存在where
                if (where == null) {
                    if (whereSql.length() > 0) {
                        Expression expression = CCJSqlParserUtil.parseCondExpression(whereSql.toString());
                        Expression whereExpression = (Expression) expression;
                        plain.setWhere(whereExpression);
                    }
                } else {
                    //把旧条件和新条件一起拼装
                    if (whereSql.length() > 0) {
                        whereSql.append(" and ( " + where.toString() + " )");
                    } else {
                        whereSql.append(where.toString());
                    }
                    Expression expression = CCJSqlParserUtil.parseCondExpression(whereSql.toString());
                    plain.setWhere(expression);
                }
                newSql = selectStatement.toString();
            }

            ExecutorPluginUtils.resetSql2Invocation(invocation, newSql);
            PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
            mpBoundSql.sql(newSql);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return invocation.proceed();
    }

    /**
     * 根据表名获取表登记息
     *
     * @param tableName 表名
     * @return
     */
    private TableInfo getTableInfo(String tableName) {
        //查找表中的列
        List<TableInfo> tableInfos = TableInfoHelper.getTableInfos();
        String finalTableName = tableName;
        String[] s = finalTableName.split(" ");
        List<TableInfo> isExistsTableList = tableInfos.stream().filter(d -> d.getTableName().equals(s[0])).collect(Collectors.toList());
        if (ListUtils.isNotExistsEle(isExistsTableList)) {
            return null;
        }
        return isExistsTableList.get(0);
    }

    /**
     * 是否存在指定列
     *
     * @param tableInfo        表信息
     * @param assignColumnList 指定列信息
     */
    private boolean isExistsAssignColumn(TableInfo tableInfo, List<String> assignColumnList) {
        if (tableInfo == null) {
            return false;
        }
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        List<String> columns = fieldList.stream().map(TableFieldInfo::getColumn).collect(Collectors.toList());
        columns.add(tableInfo.getKeyColumn());
        long count = columns.stream().filter(d -> assignColumnList.contains(d.replaceAll("`", ""))).count();
        return count > 0;
    }

    private String inSqlValue(
            String orgId,
            EnumCompanyLevel orgLv,
            EnumInsideFindDataScore findDataScore
    ) {
        BaseCompanyService baseCompanyService = ApplicationContextHelper.getBean(BaseCompanyService.class);
        String dataRangeSql = dataRangeSql = baseCompanyService.getDataRangeSql(
                orgId,
                findDataScore == EnumInsideFindDataScore.CurrentAndBelow
        );
        return dataRangeSql;
    }


    private String getValidDataLibField(CurThreadLocalUtil.ThreadStore threadStore) {
        if (StringUtils.isEmpty(threadStore.getDataLibCompanyFieldName())) {
            return OWN_ORG_ID;
        } else {
            return threadStore.getDataLibCompanyFieldName();
        }
    }


    /**
     * 是需需要过滤数据
     * true: 需要过滤
     * false: 不需要过滤
     */
    public boolean isNeedFilterData() {
        CurThreadLocalUtil.ThreadStore threadStore = CurThreadLocalUtil.getInstance().get();
        //优先级最高
        if (threadStore == null) {
            return false;
        }

        Boolean ignoreSqlSelect = threadStore.getIgnoreSqlSelect();
        //优先级比orgId高
        if (ignoreSqlSelect) {
            return false;
        }

        return StringUtils.isNotEmpty(threadStore.getOrgId());
    }
}


